# -*- coding: utf-8 -*-
"""WebRTC信令服务
基于SocketIO的WebRTC信令处理器，负责为前端和WebRTCService交换SDP和ICE候选信息
"""

from pydoc import cli
import threading
from typing import Optional
from common.config import ServiceConfig
import eventlet.queue as queue
import eventlet.semaphore
from flask import request
from common.utils import LogUtil
from web_server.device_proxy import DeviceProxy
from common.webrtc_errno import WebRTCErrno

logger = LogUtil.get_logger(__name__)

MESG_QUEUE_SIZE_LOW = 10
MESG_QUEUE_SIZE_HIGH = 100

class Mesg:
    def __init__(self, service_id: str, mesg: str):
        self.service_id = service_id
        self.mesg = mesg

class WebRTCConn:
    def __init__(self, client_id: str, device_sn: str):
        self.client_id = client_id
        self.device_sn = device_sn
        self.offer = None
        self.answer = None

class MesgService:
    """WebRTC信令服务"""
    
    def __init__(self, stop_cb: callable, device_proxy: DeviceProxy):
        self.__stop_cb = stop_cb
        self.__device_proxy = device_proxy
        self.__socketio = device_proxy.socketio
        self.__mesg_queue = queue.Queue()
        self.__socketio_clients = set()
        self.__webrtc_connections = {}
        
        self.__mesg_processor = None
        # 使用eventlet的Semaphore作为同步原语，初始值为1
        self.__processor_lock = eventlet.semaphore.Semaphore(1)
        self.__stop_flag = threading.Event()
        
        self.__register_mesg_handlers()

    def __register_mesg_handlers(self):
        """注册SocketIO消息处理器"""
        socketio = self.__socketio
        session = self.__device_proxy.session

        @socketio.on('connect')
        def handle_connect():
            """处理客户端连接事件"""
            logger.info(f'SocketIO client connected: {session.get("username", "anonymous")}') 
            # 使用eventlet的Semaphore进行同步，确保只有一个协程能创建后台任务
            # 非阻塞方式尝试获取锁，如果获取不到，说明已经有其他协程在创建或已创建后台任务
            if self.__processor_lock.acquire(blocking=False):
                try:
                    if self.__mesg_processor is None:
                        # 1. start_background_task必须在SocketIO的异步执行环境中被调用，否则不生效也不报错
                        # 2. emit必须在SocketIO的异步执行环境中被调用，否则客户端收不到消息
                        logger.info("创建消息处理后台任务")
                        self.__mesg_processor = self.__socketio.start_background_task(target=self.__process_mesg)
                finally:
                    # 释放锁，允许其他协程检查后台任务状态
                    self.__processor_lock.release()
            self.__socketio_clients.add(request.sid)

        @socketio.on('disconnect')
        def handle_disconnect():
            """处理客户端断开连接事件"""
            logger.info(f'SocketIO client disconnected: {session.get("username", "Anonymous")}')
            self.__socketio_clients.remove(request.sid)
            # 从连接字典中删除
            webrtc_conn = self.__webrtc_connections.pop(request.sid, None)
            if webrtc_conn is not None:
                client_id = webrtc_conn.client_id
                device_sn = webrtc_conn.device_sn
                def async_handle_webrtc_disconnect():
                    import eventlet
                    import asyncio
                    try:
                        # 创建一个同步包装器来运行异步协程
                        def sync_wrapper():
                            loop = asyncio.new_event_loop()
                            return loop.run_until_complete(self.__device_proxy.handle_webrtc_disconnect(client_id, device_sn))
                        # 使用eventlet spawn运行同步包装器
                        greenlet = eventlet.spawn(sync_wrapper)
                        greenlet.wait()
                    except Exception as e:
                        logger.error(f"failed to handle webrtc_disconnect: {e}")

                # 启动后台任务
                if webrtc_conn is not None:
                    socketio.start_background_task(async_handle_webrtc_disconnect)

        @socketio.on('webrtc_ready')
        def handle_webrtc_ready(data):
            """处理客户端就绪消息"""
            logger.info(f"webrtc ready: id={data['client_id']}, sn={data['device_sn']}.")
            signal_syn_seq = data['signal_syn_seq']
            signal_ack_seq = data['signal_ack_seq']
            client_id = data['client_id']
            device_sn = data['device_sn']
            webrtc_conn = WebRTCConn(client_id, device_sn)
            self.__webrtc_connections[request.sid] = webrtc_conn
            socketio.emit('webrtc_ready', {
                'signal_syn_seq': signal_ack_seq,
                'signal_ack_seq': signal_syn_seq + 1,
                'client_id': client_id,
                'device_sn': device_sn,
                'ice_servers': ServiceConfig.WEBRTC_ICE_SERVERS
            })

        @socketio.on('webrtc_offer')
        def handle_webrtc_offer(data):
            """处理客户端发送的SDP Offer消息"""
            logger.info(f"webrtc offer: id={data['client_id']}, sn={data['device_sn']}.")
            def async_handle_webrtc_offer():
                import eventlet
                import asyncio
                try:
                    # 创建一个同步包装器来运行异步协程
                    def sync_wrapper():
                        loop = asyncio.new_event_loop()
                        return loop.run_until_complete(self.__device_proxy.handle_webrtc_offer(data))
                    # 使用eventlet spawn运行同步包装器
                    greenlet = eventlet.spawn(sync_wrapper)
                    response = greenlet.wait()
                    socketio.emit(response['type'], response['data'])
                except Exception as e:
                    logger.error(f"failed to handle webrtc offer: {e}")
                    socketio.emit('webrtc_error', {
                        'signal_syn_seq': data['signal_ack_seq'],
                        'signal_ack_seq': data['signal_syn_seq'] + 1,
                        'client_id': data['client_id'],
                        'device_sn': data['device_sn'],
                        'source': 'webrtc_offer',
                        'errnum': WebRTCErrno.ESIGSVR.value,
                        'errmsg': 'Failed to forward webrtc offer to device peer'})

            # 启动后台任务
            socketio.start_background_task(async_handle_webrtc_offer)

        @socketio.on('webrtc_candidate')
        def handle_webrtc_candidate(data):
            """处理客户端发送的ICE候选消息"""
            logger.info(f"webrtc candidate: id={data['client_id']}, sn={data['device_sn']}.")
            def async_handle_webrtc_candidate():
                import eventlet
                import asyncio
                try:
                    # 创建一个同步包装器来运行异步协程
                    def sync_wrapper():
                        loop = asyncio.new_event_loop()
                        return loop.run_until_complete(self.__device_proxy.handle_webrtc_candidate(data))
                    # 使用eventlet spawn运行同步包装器
                    greenlet = eventlet.spawn(sync_wrapper)
                    response = greenlet.wait()
                    socketio.emit(response['type'], response['data'])
                except Exception as e:
                    logger.error(f"failed to handle webrtc_candidate: {e}")
                    socketio.emit('webrtc_error', {
                        'signal_syn_seq': data['signal_ack_seq'],
                        'signal_ack_seq': data['signal_syn_seq'] + 1,
                        'client_id': data['client_id'],
                        'device_sn': data['device_sn'],
                        'source': 'webrtc_candidate',
                        'errnum': WebRTCErrno.ESIGSVR.value,
                        'errmsg': 'Failed to forward webrtc candidate to device peer'})

            # 启动后台任务
            socketio.start_background_task(async_handle_webrtc_candidate)

        @socketio.on('webrtc_disconnect')
        def handle_webrtc_disconnect(data):
            """处理客户端的发送的断开连接消息"""
            logger.info(f"webrtc disconnect: id={data['client_id']}, sn={data['device_sn']}.")
            def async_handle_webrtc_disconnect():
                import eventlet
                import asyncio
                try:
                    # 创建一个同步包装器来运行异步协程
                    def sync_wrapper():
                        loop = asyncio.new_event_loop()
                        return loop.run_until_complete(self.__device_proxy.handle_webrtc_disconnect(data))
                    # 使用eventlet spawn运行同步包装器
                    greenlet = eventlet.spawn(sync_wrapper)
                    response = greenlet.wait()
                    socketio.emit(response['type'], response['data'])
                except Exception as e:
                    logger.error(f"failed to handle webrtc_disconnect: {e}")
                    socketio.emit('webrtc_error', {
                        'signal_syn_seq': data['signal_ack_seq'],
                        'signal_ack_seq': data['signal_syn_seq'] + 1,
                        'client_id': data['client_id'],
                        'device_sn': data['device_sn'],
                        'source': 'webrtc_disconnect',
                        'errnum': WebRTCErrno.ESIGSVR.value,
                        'errmsg': 'Failed to forward webrtc disconnect to device peer'})
            # 启动后台任务
            socketio.start_background_task(async_handle_webrtc_disconnect)

    def __process_mesg(self):
        while not self.__stop_flag.is_set():
            try:
                mesg = self.__mesg_queue.get(block=True, timeout=ServiceConfig.MESG_RECV_TIMEOUT)
                if len(self.__socketio_clients) == 0:
                    logger.info(f"没有客户端连接，丢弃设备事件: {mesg.mesg}")
                    if self.__mesg_queue.qsize() >= MESG_QUEUE_SIZE_HIGH:
                        while self.__mesg_queue.qsize() >= MESG_QUEUE_SIZE_LOW:
                            mesg = self.__mesg_queue.get()
                            logger.info(f"设备事件堆积过多，丢弃设备事件: {mesg.mesg}")
                    continue
                if mesg.mesg == "reload_devices":
                    logger.info(f"发射设备事件: {mesg.mesg}")
                    self.__socketio.emit('reload_devices', self.__device_proxy.load_devices())
                else:
                    logger.error(f"未知设备事件: {mesg.mesg}")
            except queue.Empty:
                # logger.info("设备事件队列为空")
                continue
            except Exception as e:
                logger.error(f"处理设备事件失败: {e}")
                if not self.__stop_flag.is_set():
                    self.__stop_flag.set()
                    if self.__stop_cb:
                        self.__stop_cb('mesg')

    def __del__(self):
        self.destroy()

    def destroy(self):
        """停止服务"""
        if not self.__stop_flag.is_set():
            self.__stop_flag.set()
            if self.__stop_cb:
                self.__stop_cb('mesg')
        
        # 使用锁保护对共享资源的修改
        with self.__processor_lock:
            if self.__mesg_processor is not None:
                # 注意：在eventlet环境中，join可能不适用于greenlet
                # 设置停止标志后，后台任务会自行结束
                self.__mesg_processor = None

    def put_mesg(self, service_id: str, mesg: str):
        logger.info(f"就绪设备事件: {mesg}")
        self.__mesg_queue.put(Mesg(service_id, mesg))